package com.choosefine.paycenter.account.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.choosefine.paycenter.account.dao.AccountBankcardMapper;
import com.choosefine.paycenter.account.dao.AccountMapper;
import com.choosefine.paycenter.account.dao.ShiXiaoBaoTrxMapper;
import com.choosefine.paycenter.account.dto.*;
import com.choosefine.paycenter.account.enums.AccountStatus;
import com.choosefine.paycenter.account.model.Account;
import com.choosefine.paycenter.account.model.AccountBalanceLog;
import com.choosefine.paycenter.account.model.ShiXiaoBaoTrx;
import com.choosefine.paycenter.account.service.AccountBalanceLogService;
import com.choosefine.paycenter.account.service.AccountService;
import com.choosefine.paycenter.common.component.RedisLock;
import com.choosefine.paycenter.common.config.ConfigAssember;
import com.choosefine.paycenter.common.constants.ExceptionCodeConstants;
import com.choosefine.paycenter.common.constants.ExceptionMessageConstants;
import com.choosefine.paycenter.common.exception.BusinessException;
import com.choosefine.paycenter.common.utils.MessageSourceUtils;
import com.choosefine.paycenter.common.utils.PasswordUtils;
import com.choosefine.paycenter.common.utils.RequestUtils;
import com.choosefine.paycenter.common.utils.SerialNumberUtils;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import tk.mybatis.mapper.entity.Condition;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.choosefine.paycenter.account.enums.RoleType.*;

/**
 * Created by Jay Chang on 2017/3/4.
 */
@Slf4j
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private AccountBankcardMapper accountBankcardMapper;

    @Autowired
    private AccountLockServiceImpl accountLockService;

    @Autowired
    private AccountBalanceLogService accountBalanceLogService;

    @Autowired
    private ShiXiaoBaoTrxMapper shiXiaoBaoTrxMapper;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    MessageSourceUtils localeMessageSourceUtils;

    @Autowired
    private ConfigAssember configAssember;

    @Value("${labour.baseUrl}")
    private String labourBaseUrl;

    @Value("${sms.baseUrl}")
    private String smsBaseUrl;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SerialNumberUtils serialNumberUtils;

    @Value("${sys.pay.payPassRetryTimes}")
    private int payPassRetryTimes;

    @Value("${sys.pay.retryWaitTime}")
    private int retryWaitTime;

    /**
     * 查询账户的角色id
     * @param accountId
     * @return
     */
    public Long findRoleIdById(final Long accountId) {
        return accountMapper.selectRoleIdById(accountId);
    }

    @Override
    public Long findRoleIdByUserCode(String userCode) {
        return accountMapper.selectRoleIdByUserCode(userCode);
    }

    /**
     * 根据租户编码查询账户id
     * @param userCode
     * @return
     */
    public Long findAccountIdByUserCode(final String userCode) {
        checkAccountExist(userCode);
        return accountMapper.selectAccountIdByUserCode(userCode);
    }


    /**
     * 绑定手机号（同时生成账号名）
     * @param userCode
     * @param mobile
     * @return
     */
    public int bindMobile(String userCode,String mobile) {
        checkAccountExist(userCode);
        final Long roleId = accountMapper.selectRoleIdByUserCode(userCode);
        String accountName = getAccountName(roleId,mobile);
        return accountMapper.bindMobile(userCode,mobile,accountName);
    }

    /**
     * 根据角色id与手机号码得到账户名
     * @param roleId
     * @param mobile
     * @return
     */
    private String getAccountName(Long roleId,String mobile){
        String name = null;
        if(WK.getValue().longValue() == roleId.longValue()){
            name = WK.name()+mobile;
        }else if(CL.getValue().longValue() == roleId.longValue()){
            name = CL.name()+mobile;
        }else if(SU.getValue().longValue() == roleId.longValue()){
            name = SU.name()+mobile;
        }else if(MG.getValue().longValue() == roleId.longValue()){
            name = MG.name()+mobile;
        }else if(CC.getValue().longValue() == roleId.longValue()){
            name = CC.name()+mobile;
        }else if(MS.getValue().longValue() == roleId.longValue()){
            name = MS.name()+mobile;
        }else if(ES.getValue().longValue() == roleId.longValue()){
            name = ES.name()+mobile;
        }else if(FS.getValue().longValue() == roleId.longValue()){
            name = FS.name()+mobile;
        }
        return name;
    }


    public void verifyPayPass(String userCode, String payPass) {
        checkAccountExist(userCode);
        Account account = accountMapper.selectByUserCode(userCode);
        if(StringUtils.isBlank(account.getPayPass())){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_PAYPASSNOTSET,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_PAYPASSNOTSET));
        }
        ValueOperations<String, String> stringValueOperations = redisTemplate.opsForValue();
        String key = ACCOUNT_PAY_PASS_INPUT_ERR_PREFIX+account.getId();
        String value = stringValueOperations.get(key);
        if(!account.getPayPass().equals(PasswordUtils.generateSecurityPayPass(payPass,account.getSalt()))){
            if(Objects.isNull(value)){
                stringValueOperations.set(key,Integer.toString(1));
            }else{
                stringValueOperations.set(key,(Integer.valueOf(value)+1)+"");
            }
            redisTemplate.expire(key,retryWaitTime,TimeUnit.MINUTES);
            int nowInputErrorTimes  = Integer.valueOf(stringValueOperations.get(key));
            if(nowInputErrorTimes >= payPassRetryTimes){
                final String lockKey=ACCOUNT_LOCK_PREFIX+account.getId();
                long seconds = 0L;
                long minutes = 10L;
                if (nowInputErrorTimes == payPassRetryTimes){
                    //锁定账户10分钟
                    accountLockService.lockAccount(account.getId());

                    throw new BusinessException(ExceptionCodeConstants.PAY_CHECK_PAYPASSTIMEERROR, localeMessageSourceUtils.getMessage(ExceptionMessageConstants.PAY_CHECK_PAYPASSTIMEERROR, new Object[]{"10", "00"}));
                }else{
                    seconds = (Long) redisTemplate.execute(new RedisCallback() {
                        @Override
                        public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                            return redisConnection.ttl(lockKey.getBytes());
                        }
                    });
                    minutes = seconds / 60;
                    seconds = seconds % 60;
                    //输入支付密码错误过多账户已被锁定，请点击忘记密码进行找回或9分钟26秒后重试
                    throw new BusinessException(ExceptionCodeConstants.PAY_CHECK_PAYPASSTIMEERROR, localeMessageSourceUtils.getMessage(ExceptionMessageConstants.PAY_CHECK_PAYPASSTIMEERROR, new Object[]{String.format("%02d",minutes), String.format("%02d",seconds)}));
                }
            }else{
                //支付密码输入不正确，您还有{0}次输入机会
                throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_PAYPASSERROR,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_PAYPASSERROR,new Object[]{payPassRetryTimes-nowInputErrorTimes}));
            }
        }else{
            redisTemplate.delete(key);
        }
    }

    /**
     * 校验账户是否锁定或冻结
     * @param accountId
     */
    public void checkAccountIsLockedOrBlocked(Long accountId) {
        checkAccountExistAndIsBlocked(accountId);
        ValueOperations<String, String> stringValueOperations = redisTemplate.opsForValue();
        String key = ACCOUNT_LOCK_PREFIX+accountId;
        String value = stringValueOperations.get(key);
        if(StringUtils.isNotBlank(value)){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_ALREADY_LOCKED,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_ALREADY_LOCKED,new Object[]{accountId}));
        }
    }

    @Override
    public String findUserCodeByAccountName(String accountName) {
        return accountMapper.selectUserCodeByAccountName(accountName);
    }

    @Override
    public boolean checkAccountNameExists(String accountName) {
        String userCode = accountMapper.selectUserCodeByAccountName(accountName);
        if(StringUtils.isBlank(userCode)){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_ACCOUNT_NAME_NOT_EXISTS,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_ACCOUNT_NAME_NOT_EXISTS));
        }
        return true;
    }

    @Override
    @Transactional
    public void clearAccountNameAndMobile(AccountClearNameAndMobileDto accountClearNameAndMobileDto) {
        List<UserItemDto> userList = accountClearNameAndMobileDto.getUserList();
        if(org.apache.commons.collections.CollectionUtils.isNotEmpty(userList)) {
            for (UserItemDto user : userList) {
                int affectedRows = accountMapper.updateAccountNameAndMobileAsNullByUserCode(user.getUserCode(),user.getRoleId());
                if(affectedRows <= 0){
                    throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CLEAR_NAME_AND_MOBILE_CANNOT_FOUND,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CLEAR_NAME_AND_MOBILE_CANNOT_FOUND,new Object[]{user.getUserCode(),user.getRoleId()}));
                }
            }
        }
    }

    @Override
    @Transactional
    public void modifyAccountNameAndMobile(AccountModifyNameAndMobileDto accountModifyNameAndMobileDto) {
        List<UserItemDto> userList = accountModifyNameAndMobileDto.getUserList();
        if(org.apache.commons.collections.CollectionUtils.isNotEmpty(userList)) {
            for (UserItemDto user : userList) {
                String accountName = getAccountName(user.getRoleId(),accountModifyNameAndMobileDto.getNewMobile());
                int affectedRows = accountMapper.updateAccountNameAndMobileByUserCode(accountName,accountModifyNameAndMobileDto.getNewMobile(),user.getUserCode(),user.getRoleId());
                if(affectedRows <= 0){
                    throw new BusinessException(ExceptionCodeConstants.ACCOUNT_MODIFY_NAME_AND_MOBILE_CANNOT_FOUND,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_MODIFY_NAME_AND_MOBILE_CANNOT_FOUND,new Object[]{user.getUserCode(),user.getRoleId()}));
                }
            }
        }
    }

    public @interface AccountAdd{}
    /**
     * 新增资金账户
     *
     * @param accountDto 资金账户
     */
    public Long saveAccount(final AccountDto accountDto){
        Account checkAccountExists = accountMapper.selectByUserCode(accountDto.getUserCode());
        if(null != checkAccountExists){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_ALREADYEXIST,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_ALREADYEXIST));
        }
        Account account = new Account();
        BeanUtils.copyProperties(accountDto,account);
        accountMapper.insertSelective(account);
        return account.getId();
    }

    /**
     * 清空支付密码
     *
     * @param userCode 资金账户的userCode
     */
    public int clearPayPass(final String userCode){
        checkAccountExist(userCode);
        Account account = accountMapper.selectByUserCode(userCode);

        try {
            String url = smsBaseUrl + configAssember.getRestUrl(API_SEND_SMS_GET);
            Map<String, Object> params = Maps.newHashMap();
            params.put("phone", account.getMobile());
            params.put("smsTemplateName", SMS_TEMPLATE);
            params.put("params", PRODUCT);
            JSONObject result = RequestUtils.getInstance().getForString(restTemplate, url, params);
            log.info("clearPayPass accountId{}，sendSms result {}", account.getId(), result);
        }catch (Exception e){
            log.error("清空支付密码，短信发送失败",e);
        }
        int res= accountMapper.updatePayPassAsNull(account.getId());
        if(res>0){
            redisTemplate.delete(ACCOUNT_PAY_PASS_INPUT_ERR_PREFIX+account.getId());
            accountLockService.unlockAccount(account.getId());//清空密码成功即解锁该账户
        }
        return res;
    }


    /**
     * 查询资金账号详情
     * @param id
     * @return
     */
    public Account findById(final Long id){
        Account account =  accountMapper.selectByPrimaryKey(id);
        if(Objects.isNull(account)){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_NOTEXIST,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_NOTEXIST));
        }
        doSetBankcardNumAndIsSetPayPass(account);
        return account;
    }

    /**
     * 查询资金账号详情
     * @param userCode
     * @return
     */
    public Account findByUserCode(final String userCode){
        Account account =  accountMapper.selectByUserCode(userCode);
        if(Objects.isNull(account)){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_NOTEXIST,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_NOTEXIST));
        }
        if(StringUtils.isNotBlank(account.getPayPass())){
            account.setIsSetPayPass(true);
        }
        doSetBankcardNumAndIsSetPayPass(account);
        return account;
    }

    /**
     * 设置银行卡数量及是否已设置支付密码的标志
     * @param account
     */
    private void doSetBankcardNumAndIsSetPayPass(final Account account) {
        int bankcardNum = accountBankcardMapper.selectBankcardNumByAccountId(account.getId());
        account.setBankcardNum(bankcardNum);
        account.setIsSetPayPass(StringUtils.isNotBlank(account.getPayPass())? true:false);
    }

    /**
     * 查询资金账户总金额(即查询总账户金额)
     * @return
     */
    public BigDecimal findTotalAmount(){
        return accountMapper.selectTotalAmount();
    }

    /**
     * 分页多条件查询资金账户列表
     * @param accountDto
     * @return
     */
    public PageInfo<Account> findAllByPage(final AccountDto accountDto){
        Condition condition = new Condition(Account.class);
        Example.Criteria criteria = condition.createCriteria();
        if(StringUtils.isNotBlank(accountDto.getUserCode())){
            criteria.andLike("userCode","%"+accountDto.getUserCode()+"%");
        }
        if(StringUtils.isNotBlank(accountDto.getRealName())){
            criteria.andLike("realName","%"+accountDto.getRealName()+"%");
        }
        if(Objects.nonNull(accountDto.getRoleId())){
            criteria.andEqualTo("roleId",accountDto.getRoleId());
        }
        if(Objects.nonNull(accountDto.getStatus())){
            criteria.andEqualTo("status",accountDto.getStatus());
        }
        Page<Account> page = PageHelper.startPage(accountDto.getPageNum(),accountDto.getPageSize());
        accountMapper.selectByExample(condition);
        if(!CollectionUtils.isEmpty( page.getResult())) {
            for (Account account : page.getResult()) {
                if(StringUtils.isNotBlank(account.getPayPass())){
                    account.setIsSetPayPass(true);
                }
                doSetBankcardNumAndIsSetPayPass(account);
            }
        }
        return new PageInfo<>(page);
    }

    /**
     * 冻结账户
     * @param userCode
     */
    public int blockAccount(final String userCode) {
        checkAccountExist(userCode);
        Long id = accountMapper.selectAccountIdByUserCode(userCode);
        return accountMapper.updateStatusById(id, AccountStatus.BLOCKED.toString());
    }

    /**
     * 解冻资金账户
     * @param userCode
     * @return
     */
    public int unBlockAccount(final String userCode) {
        checkAccountExist(userCode);
        Long id = accountMapper.selectAccountIdByUserCode(userCode);
        return accountMapper.updateStatusById(id,AccountStatus.NORMAL.toString());
    }

    /**
     * 解冻资金账户
     * @param accountId
     * @return
     */
    public int unBlockAccount(final Long accountId) {
        return accountMapper.updateStatusById(accountId,AccountStatus.NORMAL.toString());
    }

    /**
     * 查询资金账户余额
     * @param id
     * @return
     */
    public BigDecimal findBalanceById(Long id) {
        checkAccountExist(id);
        return accountMapper.selectBalanceById(id);
    }

    public BigDecimal findAvailableBalanceById(Long id){
        checkAccountExist(id);
        return accountMapper.selectAvailableBalanceById(id);
    }


    /**
     * 查询资金账户余额
     * @param userCode
     * @return
     */
    public BigDecimal findBalanceByUserCode(String userCode) {
        checkAccountExist(userCode);
        return accountMapper.selectBalanceByUserCode(userCode);
    }

    /**
     * 根据资金账号id校验某个资金账号是否存在
     * @param id
     */
    public void checkAccountExist(final Long id) {
        int count = accountMapper.selectExistById(id);
        if(count <= 0){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_NOTEXIST,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_NOTEXIST));
        }
    }

    /**
     * 校验某个租户编码的账号是否存在
     * @param userCode
     */
    public void checkAccountExist(final String userCode) {
        int count = accountMapper.selectExistByUserCode(userCode);
        if(count <= 0){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_NOTEXIST,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_NOTEXIST));
        }
    }

    /**
     * 校验资金账户是否存在且是否被冻结和锁定
     *
     * @param id
     */
    public void checkAccountExistAndIsBlocked(final Long id) {
        Account account = accountMapper.selectByPrimaryKey(id);
        if(Objects.isNull(account)){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_NOTEXIST,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_NOTEXIST));
        }
        checkAccountIsLocked(id);
        //是否冻结
        if(AccountStatus.BLOCKED.equals(account.getStatus())){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_ALREADYBLOCK,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_ALREADYBLOCK,account.getRealName()));
        }
    }

    private void checkAccountIsLocked(Long id) {
        //是否锁定
        if(accountLockService.isLockAccount(id)){
            final String lockKey= AccountService.ACCOUNT_LOCK_PREFIX+id;
            long seconds = (Long) redisTemplate.execute(new RedisCallback() {
                @Override
                public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                    return redisConnection.ttl(lockKey.getBytes());
                }
            });
            long minutes = seconds / 60;
            seconds = seconds % 60;
            //输入支付密码错误过多账户已被锁定，请点击忘记密码进行找回或9分钟26秒后重试
            throw new BusinessException(ExceptionCodeConstants.PAY_CHECK_PAYPASSTIMEERROR, localeMessageSourceUtils.getMessage(ExceptionMessageConstants.PAY_CHECK_PAYPASSTIMEERROR, new Object[]{String.format("%02d",minutes), String.format("%02d",seconds)}));
        }
    }

    /**
     * 校验交易主体资金账号余额是否充足
     */
    public void checkBalanceIsEnough(Long mainAccountId,BigDecimal amount){
        Account mainAccount = accountMapper.selectByPrimaryKey(mainAccountId);
        //这里需要判断可用余额是否充足
        if(mainAccount.getAvailableBalance().compareTo(amount) < 0){
            throw new BusinessException(ExceptionCodeConstants.PAYTYPE_BALANCE_NOTENOUGH,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.PAYTYPE_BALANCE_NOTENOUGH));
        }
    }

    /**
     * 校验提现的账户是否跟登陆账户匹配
     */
    public void checkUserIsRight(Long mainAccountId,String userCode){
        Account mainAccount = accountMapper.selectByPrimaryKey(mainAccountId);
        if(!mainAccount.getUserCode().equals(userCode)){
            throw new BusinessException(ExceptionCodeConstants.WITHDRAW_ACCOUNT_IS_NOT_MATCH,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.WITHDRAW_ACCOUNT_IS_NOT_MATCH));

        }
    }
    /**
     * 未设置过支付密码的账户可以初始化支付密码
     * @param accountDto 资金账户Dto
     */
    public int initPayPass(AccountDto accountDto) {
        checkAccountExist(accountDto.getUserCode());
        Long acctId = accountMapper.selectAccountIdByUserCode(accountDto.getUserCode());
        checkAccountIsLocked(acctId);
        String salt = PasswordUtils.createSalt();
        String securityPayPass = PasswordUtils.generateSecurityPayPass(accountDto.getPayPass(),salt);
        return accountMapper.updatePayPassById(acctId,securityPayPass,salt);

    }
    public @interface InitPayPass{}

    /**
     * 修改支付密码
     *
     * @param accountDto
     */
    public int editPayPass(AccountDto accountDto) {
        Account account = accountMapper.selectByUserCode(accountDto.getUserCode());
        //校验账户是否已经被锁定
        checkAccountIsLocked(account.getId());
        if(Objects.isNull(account)){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_NOTEXIST,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_NOTEXIST));
        }
        if(!account.getPayPass().equals(PasswordUtils.generateSecurityPayPass(accountDto.getOldPayPass(),account.getSalt()))){
            throw new BusinessException(ExceptionCodeConstants.ACCOUNT_CHECK_OLDPAYPASSERROR,localeMessageSourceUtils.getMessage(ExceptionMessageConstants.ACCOUNT_CHECK_OLDPAYPASSERROR));

        }
        String salt = PasswordUtils.createSalt();
        String securityPayPass = PasswordUtils.generateSecurityPayPass(accountDto.getPayPass(),salt);
        return accountMapper.updatePayPassById(account.getId(),securityPayPass,salt);
    }
    public @interface EditPayPass{}

    /**
     * 查询施小包流水
     * @return
     */
    public PageInfo<ShiXiaoBaoTrx> findShiXiaoBaoTrxHistoryByPage(ShiXiaoBaoTrxDto shiXiaoBaoTrxDto){
        Condition condition = new Condition(ShiXiaoBaoTrx.class);
        Example.Criteria criteria = condition.createCriteria();
        if (null != shiXiaoBaoTrxDto.getStartTime()) {
            criteria.andGreaterThanOrEqualTo("createdAt", DateFormatUtils.format(shiXiaoBaoTrxDto.getStartTime(),"yyyy-MM-dd HH:mm:ss"));
        }
        if (null != shiXiaoBaoTrxDto.getEndTime()) {
            criteria.andLessThanOrEqualTo("createdAt", DateFormatUtils.format(shiXiaoBaoTrxDto.getEndTime(),"yyyy-MM-dd HH:mm:ss"));
        }
        if(null != shiXiaoBaoTrxDto.getFundFlowDirection()){
            criteria.andEqualTo("type",shiXiaoBaoTrxDto.getFundFlowDirection());
        }
        Page<ShiXiaoBaoTrx> page = PageHelper.startPage(shiXiaoBaoTrxDto.getPageNum(),shiXiaoBaoTrxDto.getPageSize());
        shiXiaoBaoTrxMapper.selectByExample(condition);
        return new PageInfo<>(page);
    }

    public @interface VerifyPayPass {
    }
    /**
     * 更新资金账户余额
     * @param accountId
     * @param balance
     * @return
     */
    public int updateAccountBalance(Long accountId,BigDecimal balance){
        return accountMapper.updateBalanceById(accountId,balance);
    }

    /**
     * 根据租户编码查询资金账户详情
     * @param userCode
     * @return
     */
    public Account findAccountByUserCode(String userCode){
        return accountMapper.selectByUserCode(userCode);
    }

    /**
     * 增加主账户余额(用于充值)
     * @param mainDto
     * @param sn
     */
    public void increaseMainAccountBalance(MainDto mainDto,String sn) {
        RedisLock mainAccountLock = null;
        try {
            mainAccountLock = new RedisLock(redisTemplate,UNIQUE_ACCOUNT_BALANCE+mainDto.getUserCode());
            Account mainAccount = accountMapper.selectByPrimaryKey(mainDto.getAccountId());

            BigDecimal oldFreezeBalance = mainAccount.getFreezeBalance();
            BigDecimal oldAvailableBalance = mainAccount.getAvailableBalance();
            BigDecimal oldBalance = mainAccount.getBalance();
            BigDecimal newFreezeBalance = oldFreezeBalance;
            BigDecimal newAvailableBalance = oldAvailableBalance.add(mainDto.getAmount());
            BigDecimal newBalance = oldBalance.add(mainDto.getAmount());

            //余额变更日志
            AccountBalanceLog accountBalanceLog = new AccountBalanceLog();
            accountBalanceLog.setAccountId(mainDto.getAccountId());
            accountBalanceLog.setAccountRealName(mainDto.getAccountRealName());
            accountBalanceLog.setAccountUserCode(mainDto.getUserCode());
            accountBalanceLog.setOldFreezeBalance(oldFreezeBalance);
            accountBalanceLog.setOldAvailableBalance(oldAvailableBalance);
            accountBalanceLog.setOldBalance(oldBalance);
            accountBalanceLog.setNewFreezeBalance(newFreezeBalance);
            accountBalanceLog.setNewAvailableBalance(newAvailableBalance);
            accountBalanceLog.setNewBalance(newBalance);
            accountBalanceLog.setChangeDesc("充值流水号["+sn+"],账户可用余额+"+mainDto.getAmount()+",总余额+"+mainDto.getAmount());
            accountBalanceLogService.recordAccountBalanceLog(accountBalanceLog);

            //更新资金账户可用余额，总余额
            accountMapper.updateAvailableBalanceAndBalanceById(mainDto.getAccountId(), newAvailableBalance, newBalance);
        }catch (Exception e){
            log.error("更新账户余额失败,流水号："+sn,e);
            throw new RuntimeException(e);
        }finally {
            if(null != mainAccountLock){
                mainAccountLock.unlock();
            }
        }
    }

    /**
     * 减少主账号的余额（用于自身渠道（余额转账））
     * @param mainDto
     */
    public void decreaseMainAccountBalance(MainDto mainDto,String sn) {
        RedisLock mainAccountLock = null;
        try {
            mainAccountLock = new RedisLock(redisTemplate, UNIQUE_ACCOUNT_BALANCE+mainDto.getUserCode());
            mainAccountLock.lock();
            Account mainAccount = accountMapper.selectByPrimaryKey(mainDto.getAccountId());
            //写交易主账号余额变更日志
            AccountBalanceLog accountBalanceLog = new AccountBalanceLog();
            accountBalanceLog.setAccountId(mainAccount.getId());
            accountBalanceLog.setAccountRealName(mainAccount.getRealName());
            accountBalanceLog.setAccountUserCode(mainAccount.getUserCode());
            BigDecimal oldFreezeBalance = mainAccount.getFreezeBalance();
            BigDecimal oldAvailableBalance = mainAccount.getAvailableBalance();
            BigDecimal oldBalance = mainAccount.getBalance();
            BigDecimal newFreezeBalance = oldFreezeBalance;
            BigDecimal newAvailableBalance = oldAvailableBalance.subtract(mainDto.getAmount());
            BigDecimal newBalance = oldBalance.subtract(mainDto.getAmount());
            accountBalanceLog.setOldFreezeBalance(oldFreezeBalance);
            accountBalanceLog.setOldAvailableBalance(oldAvailableBalance);
            accountBalanceLog.setOldBalance(oldBalance);
            accountBalanceLog.setNewFreezeBalance(newFreezeBalance);
            accountBalanceLog.setNewAvailableBalance(newAvailableBalance);
            accountBalanceLog.setNewBalance(newBalance);
            accountBalanceLog.setChangeDesc("转账流水号["+sn+"],交易主账户可用余额-"+mainDto.getAmount()+",总余额-"+mainDto.getAmount());
            accountBalanceLogService.recordAccountBalanceLog(accountBalanceLog);

            //更改账户余额
            accountMapper.updateAvailableBalanceAndBalanceById(mainAccount.getId(),newAvailableBalance,newBalance);

        } catch (Exception e){
            log.error("更新账户余额失败，流水号：+"+sn,e);
            throw new RuntimeException(e);
        }finally {
            if(null != mainAccountLock) {
                mainAccountLock.unlock();
            }
        }
    }

    /**
     * 增加对方账户余额
     * @param OrdersDtoList
     */
    @Transactional
    public  void increaseOppositeAccountBalance(List<OrderDto> orderDtoList,String sn){
        List<RedisLock> oppositeAccountLockList = Lists.newArrayList();
        try {
            if(!CollectionUtils.isEmpty(orderDtoList)) {
                for (OrderDto ordersDto : orderDtoList) {
                    RedisLock oppositeLock = new RedisLock(redisTemplate, UNIQUE_ACCOUNT_BALANCE + ordersDto.getUserCode());
                    oppositeAccountLockList.add(oppositeLock);
                    oppositeLock.lock();

                    Account account = accountMapper.selectByUserCode(ordersDto.getUserCode());
                    //写交易账号余额变更日志
                    AccountBalanceLog accountBalanceLog = new AccountBalanceLog();
                    accountBalanceLog.setAccountId(account.getId());
                    accountBalanceLog.setAccountRealName(account.getRealName());
                    accountBalanceLog.setAccountUserCode(account.getUserCode());
                    BigDecimal oldFreezeBalance = account.getFreezeBalance();
                    BigDecimal oldAvailableBalance = account.getAvailableBalance();
                    BigDecimal oldBalance = account.getBalance();
                    BigDecimal newFreezeBalance = oldFreezeBalance;
                    BigDecimal newAvailableBalance = oldAvailableBalance.add(ordersDto.getAmount());
                    BigDecimal newBalance = oldBalance.add(ordersDto.getAmount());
                    accountBalanceLog.setOldFreezeBalance(oldFreezeBalance);
                    accountBalanceLog.setOldAvailableBalance(oldAvailableBalance);
                    accountBalanceLog.setOldBalance(oldBalance);
                    accountBalanceLog.setNewFreezeBalance(newFreezeBalance);
                    accountBalanceLog.setNewAvailableBalance(newAvailableBalance);
                    accountBalanceLog.setNewBalance(newBalance);
                    accountBalanceLog.setChangeDesc("转账流水号["+sn+"],交易对方账户可用余额+"+ordersDto.getAmount()+",总余额+"+ordersDto.getAmount());
                    accountBalanceLogService.recordAccountBalanceLog(accountBalanceLog);

                    //更改账户余额
                    accountMapper.updateAvailableBalanceAndBalanceById(account.getId(),newAvailableBalance,newBalance);


                }
            }
        }catch (Exception e){
            log.error("更新账户余额失败，流水号：+"+sn, e);
            throw new RuntimeException(e);
        }finally {
            for(RedisLock lock : oppositeAccountLockList){
                lock.unlock();
            }
        }
    }

    @Override
    public int updateAccountBalanceBeforWithDraw(AccountWdDto accountDTO) {
        RedisLock mainAccountLock = null;
        try {
            mainAccountLock = new RedisLock(redisTemplate, UNIQUE_ACCOUNT_BALANCE+accountDTO.getUserCode());
            mainAccountLock.lock();
            int update=accountMapper.updateAccountBalanceBeforWithDraw(accountDTO.getAccountId(),accountDTO.getBalance(),accountDTO.getFreezeBalance(),accountDTO.getAvailableBalance());
            return update;
        }catch (Exception e){
            log.error("更新账户余额失败");
        }finally {
            if(null != mainAccountLock) {
                mainAccountLock.unlock();
            }
        }
        return 0;
    }

}
